from fastapi import APIRouter, Depends, HTTPException, status, Query
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime, timedelta
from enum import Enum
from sqlalchemy.orm import Session
from sqlalchemy import func, or_, and_
import hashlib
import logging

from database.models import User, UserLevel, CompetitionWork, UserFollow, UserNotification, UserLike, Banner, LoginSession
from database.connection import get_db
logger = logging.getLogger(__name__)

# Enum definitions
class LoginType(str, Enum):
    PASSWORD = "password"
    SMS = "sms"
    WECHAT = "wechat"

class RegisterType(str, Enum):
    PASSWORD = "password"
    SMS = "sms"
    WECHAT = "wechat"

class UserStatus(str, Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    BANNED = "banned"

# Pydantic model definitions
from models.auth import (
    LoginType, RegisterType, UserStatus,
    UserRegister, UserLogin, UserResponse, UserUpdate,
    TokenResponse, SMSCodeRequest, EmailCodeRequest,
    WeChatLoginRequest, BindAccountRequest, UnbindAccountRequest,
    UserMeResponse, FollowListResponse, FollowRequest, FollowResponse,
    BackgroundCoverUpdate, BackgroundCoverResponse,
    LoginResponseWrapper, LoginData,
)
from utils.files import safe_replace_file
from utils.auth import (
    verify_password, get_password_hash,
    get_current_user_id, create_user_token, validate_phone_number,
    validate_email, generate_verification_code
)
from utils.sms import send_sms_code, verify_sms_code
from utils.wechat import wechat_service
from utils.email import send_email_verification_code, verify_email_code
from config import get_settings
from urllib.parse import quote_plus, unquote_plus
from fastapi import Request
from fastapi.responses import RedirectResponse
import json
import xml.etree.ElementTree as ET
from datetime import timedelta
from database.models import LoginSession, OauthState
import uuid
import random
from lxml import etree  # 需要安装：pip install lxml

router = APIRouter(prefix="/auth", tags=["用户认证"])
security = HTTPBearer(auto_error=False)


async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)):
    """
    获取当前用户信息
    
    Args:
        credentials: HTTP认证凭据
        db: 数据库会话
    
    Returns:
        User: 用户对象
    
    Raises:
        HTTPException: 用户不存在时抛出401错误
    """
    # 检查是否提供了认证凭据
    if credentials is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="未提供认证令牌",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    # 验证JWT token并获取用户ID
    user_id = get_current_user_id(credentials)
    
    # 从数据库获取用户信息
    user = db.query(User).filter(User.id == user_id, User.is_delete == False).first()
    
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户不存在",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    return user


def create_welcome_notification(db: Session, user_id: int) -> None:
    """
    为指定用户创建一条系统欢迎通知
    
    Args:
        db: 数据库会话
        user_id: 新注册用户ID
    Returns:
        None
    """
    try:
        notif = UserNotification(
            user_id=str(user_id),
            sender_id=None,
            notification_type=1,
            title="欢迎加入 aigd",
            content="欢迎你加入aigd的大家庭！请记得补充自己的信息",
            related_id=None,
            related_type=None,
            is_read_id=None,
            is_delete_id=None,
        )
        db.add(notif)
        db.commit()
    except Exception as e:
        try:
            db.rollback()
        except Exception:
            pass
        logger.error(f"创建欢迎通知失败: user_id={user_id}, error={str(e)}")


@router.post("/register", response_model=TokenResponse, summary="用户注册")
async def register(user_data: UserRegister, db: Session = Depends(get_db)):
    """
    用户注册接口
    支持用户名+密码、手机号+验证码注册
    
    Args:
        user_data: 用户注册数据
        db: 数据库会话
    
    Returns:
        TokenResponse: 包含访问令牌和用户信息的响应
    
    Raises:
        HTTPException: 参数校验失败或注册失败时抛出异常
    """
    
    if user_data.register_type == RegisterType.PASSWORD:
        # 用户名密码注册
        if not user_data.username or not user_data.password:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="请输入用户名和密码"
            )
        
        # 检查用户名是否已存在
        existing_user = db.query(User).filter(
            User.username == user_data.username,
            User.is_delete == False
        ).first()
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="用户名已存在"
            )
    
    elif user_data.register_type == RegisterType.SMS:
        # 手机号验证码注册
        if not user_data.phone or not user_data.sms_code:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="请输入手机号和验证码"
            )
        
        if not validate_phone_number(user_data.phone):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号格式不正确"
            )
        
        # 检查手机号是否已存在
        existing_phone = db.query(User).filter(
            User.phone == user_data.phone,
            User.is_delete == False
        ).first()
        if existing_phone:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号已注册"
            )
        
        # 验证短信验证码
        if not await verify_sms_code(user_data.phone, user_data.sms_code):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="短信验证码错误或已过期"
            )
    
    else:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="不支持的注册方式"
        )
    
    # 检查邮箱是否已存在（可选字段）
    if user_data.email:
        if not validate_email(user_data.email):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱格式不正确"
            )
        
        existing_email = db.query(User).filter(
            User.email == user_data.email,
            User.is_delete == False
        ).first()
        if existing_email:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱已注册"
            )
    
    # 创建新用户
    hashed_password = None
    if user_data.password:
        hashed_password = get_password_hash(user_data.password)
    
    new_user = User(
        username=user_data.username,
        password=hashed_password,
        phone=user_data.phone,
        email=user_data.email,
        nickname=user_data.nickname or user_data.username,
        create_time=datetime.now(),
        update_time=datetime.now()
    )
    
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    try:
        create_welcome_notification(db, new_user.id)
    except Exception:
        pass
    
    # 生成访问令牌
    token_data = create_user_token(new_user.id, new_user.username)
    
    return TokenResponse(
        code=200,
        access_token=token_data["access_token"],
        token_type=token_data["token_type"],
        user=UserResponse(
            id=new_user.id,
            username=new_user.username,
            nickname=new_user.nickname,
            phone=new_user.phone,
            email=new_user.email,
            avatar=new_user.avatar,
            background_cover=new_user.background_cover,
            create_time=new_user.create_time
        )
    )


@router.get("/wechat/qr", summary="生成公众号临时二维码")
async def wechat_qr(expire_seconds: int = Query(300, ge=60, le=1800, description="二维码有效期秒数"), db: Session = Depends(get_db)):
    """
    生成用于网页扫码登录的公众号临时二维码（后端生成并返回session_id）
    """
    import random, uuid
    session_id = uuid.uuid4().hex
    scene_id = random.randint(10000000, 99999999)
    expire_time = datetime.now() + timedelta(seconds=expire_seconds)
    sess = LoginSession(session_id=session_id, scene_id=scene_id, status="pending", expire_time=expire_time)
    db.add(sess)
    db.commit()

    qr = await wechat_service.create_qr_scene(scene_id, expire_seconds)
    if not qr or (isinstance(qr, dict) and qr.get("error")):
        detail = "生成二维码失败"
        if isinstance(qr, dict) and qr.get("error"):
            # 透出上游错误信息，常见为微信接口的IP白名单限制
            detail = f"微信接口错误：{qr.get('error')}"
        raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=detail)

    return {"code": 200, "msg": "二维码生成成功", "data": {"session_id": session_id, "scene_id": scene_id, "qr_url": qr["qr_url"], "expire_seconds": qr["expire_seconds"]}}


@router.get("/wechat/poll", summary="轮询扫码登录状态")
async def wechat_poll(session_id: str = Query(..., description="网页会话ID"), db: Session = Depends(get_db)):
    """
    轮询指定会话ID的扫码登录状态
    """
    session = db.query(LoginSession).filter(LoginSession.session_id == session_id).first()
    if not session or session.expire_time < datetime.now():
        return {"code": 200, "msg": "二维码已过期", "data": {"status": "expired"}}
    # 细化中间态：当已拿到 openid 但未绑定 user_id 时，返回 scanned
    if session.status != "authed":
        if session.openid and not session.user_id:
            return {"code": 200, "msg": "已扫码，正在授权", "data": {"status": "scanned"}}
        return {"code": 200, "msg": "等待扫码", "data": {"status": session.status}}
    user_id = session.user_id

    user = db.query(User).filter(User.id == int(user_id), User.is_delete == False).first()
    if not user:
        return {"code": 200, "msg": "用户不存在", "data": {"status": "error"}}

    user_level = None
    if user.points is not None:
        user_level = db.query(UserLevel).filter(UserLevel.required_points <= user.points).order_by(UserLevel.required_points.desc()).first()
    work_count = db.query(CompetitionWork).filter(CompetitionWork.user_id == user.id).count()
    follow_count = db.query(UserFollow).filter(UserFollow.follower_id == user.id, UserFollow.is_delete == False).count()
    fans_count = db.query(UserFollow).filter(UserFollow.following_id == user.id, UserFollow.is_delete == False).count()

    token_data = create_user_token(user.id, user.username or user.nickname)
    return {
        "code": 200,
        "msg": "已扫码",
        "data": {
            "status": session.status,
            "access_token": token_data["access_token"],
            "token_type": token_data["token_type"],
            "user": {
                "id": user.id,
                "points": user.points,
                "username": user.username,
                "nickname": user.nickname,
                "phone": user.phone,
                "email": user.email,
                "avatar": user.avatar or user.wx_avatar,
                "background_cover": user.background_cover,
                "create_time": user.create_time,
                "level_id": (user_level.level_id if user_level else None),
                "level_name": (user_level.level_name if user_level else None),
                "level_icon": (user_level.url if user_level else None),
                "level_icon2": (user_level.url2 if user_level else None),
                "work_count": work_count,
                "follow_count": follow_count,
                "fans_count": fans_count
            }
        }
    }


@router.api_route("/wechat/event", methods=["GET", "POST"], summary="公众号事件推送回调")
async def wechat_event(
    request: Request,
    format: Optional[str] = Query("text", description="返回格式：text/json"),
    db: Session = Depends(get_db)
):
    """
    处理微信公众号服务器配置的事件推送（扫码/关注）
    
    Args:
        request: 请求对象
        format: 返回格式（默认text，支持json）
        db: 数据库会话
    
    Returns:
        当format=text：返回字符串（GET返回echostr，POST返回success）
        当format=json：返回统一结构 {code,msg,data}
    """
    settings = get_settings()
    if request.method == "GET":
        params = request.query_params
        signature = params.get("signature")
        timestamp = params.get("timestamp")
        nonce = params.get("nonce")
        echostr = params.get("echostr")
        from utils.wechat import validate_wechat_signature
        if validate_wechat_signature(signature or "", timestamp or "", nonce or "", token=settings.wechat_token or ""):
            # 微信验证必须原样返回echostr
            if echostr:
                return echostr
            # 非微信调用时支持JSON返回
            if format == "json":
                return {"code": 200, "msg": "签名验证通过"}
            return ""
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="签名验证失败")

    body = await request.body()
    try:
        logger.info(f"微信事件入站: method=POST, len={len(body)}, qs={dict(request.query_params)}")
    except Exception:
        pass
    try:
        root = ET.fromstring(body)
    except Exception:
        return {"code": 200, "msg": "忽略非XML事件"} if format == "json" else "success"

    event = root.findtext("Event") or ""
    from_user = root.findtext("FromUserName") or ""
    event_key = root.findtext("EventKey") or ""
    to_account = root.findtext("ToUserName") or ""
    # 加密消息处理：存在 Encrypt 时尝试解密
    encrypt_text = root.findtext("Encrypt") or ""
    if encrypt_text:
        params = request.query_params
        settings = get_settings()
        xml_data = decrypt_wechat_xml(
            encrypt_text=encrypt_text,
            msg_signature=params.get("msg_signature") or "",
            timestamp=params.get("timestamp") or "",
            nonce=params.get("nonce") or "",
            token=settings.wechat_token or "",
            aes_key=settings.wechat_encoding_aes_key or "",
            app_id=settings.wechat_original_id or ""
        )
        if not xml_data:
            logger.warning("加密消息解密失败或配置缺失，忽略事件")
            return "success"
        try:
            root = ET.fromstring(xml_data)
        except Exception as parse_err:
            logger.error(f"解密后XML解析失败: {str(parse_err)}")
            return "success"
        event = root.findtext("Event") or ""
        from_user = root.findtext("FromUserName") or ""
        event_key = root.findtext("EventKey") or ""
        to_account = root.findtext("ToUserName") or ""

    scene_id = None
    if event_key.startswith("qrscene_"):
        try:
            scene_id = int(event_key.split("qrscene_")[1])
        except Exception:
            scene_id = None
    else:
        try:
            scene_id = int(event_key)
        except Exception:
            scene_id = None

    if not scene_id or not from_user:
        return {"code": 200, "msg": "缺少scene或openid"} if format == "json" else "success"

    session = db.query(LoginSession).filter(LoginSession.scene_id == scene_id).first()
    if not session or session.expire_time < datetime.now():
        return {"code": 200, "msg": "会话不存在或已过期"} if format == "json" else "success"

    # 先写入中间态 scanned：标记已扫码但尚未完成用户绑定
    session.openid = from_user
    session.status = "scanned"
    db.commit()

    user = db.query(User).filter(User.openid == from_user, User.is_delete == False).first()
    if not user:
        info = await wechat_service.get_public_user_info(from_user)
        wx_sex = (info or {}).get('sex')
        gender_val = 1 if wx_sex == 1 else (2 if wx_sex == 2 else 3)
        user = User(
            openid=from_user,
            wx_nickname=(info or {}).get('nickname'),
            wx_avatar=(info or {}).get('headimgurl'),
            nickname=((info or {}).get('nickname') or f"微信用户_{from_user[:8]}"),
            gender=gender_val,
            province=(info or {}).get('province'),
            city=(info or {}).get('city'),
            country=(info or {}).get('country'),
            create_time=datetime.now(),
            update_time=datetime.now()
        )
        db.add(user)
        db.commit()
        db.refresh(user)
        try:
            create_welcome_notification(db, user.id)
        except Exception:
            pass

    user.login_time = datetime.now()
    db.commit()

    # 完成绑定后置为 authed
    session.status = "authed"
    session.user_id = user.id
    db.commit()

    if format == "json":
        return {"code": 200, "msg": "success", "data": {"scene_id": scene_id, "session_id": session.session_id, "user_id": user.id}}
    if to_account and from_user:
        return build_text_reply_xml(from_user, to_account, "扫码成功，欢迎登录！")
    try:
        msg_ok = await wechat_service.send_custom_text_message(from_user, "扫码成功，欢迎登录！")
        if not msg_ok:
            logger.warning(f"客服消息推送失败，openid={from_user}")
    except Exception as push_err:
        logger.error(f"客服消息推送异常: {str(push_err)}")
    return "success"


# 兼容：将 /wechat/verify 作为事件回调的别名路径，避免后台只配置一个URL导致POST事件回调无法命中
@router.api_route("/wechat/verify", methods=["GET", "POST"], summary="微信服务器签名验证/事件回调")
async def wechat_verify(
    request: Request,
    format: Optional[str] = Query("text", description="返回格式：text/json"),
    db: Session = Depends(get_db)
):
    settings = get_settings()
    if request.method == "GET":
        params = request.query_params
        signature = params.get("signature") or ""
        timestamp = params.get("timestamp") or ""
        nonce = params.get("nonce") or ""
        echostr = params.get("echostr") or ""
        token = settings.wechat_token or ""
        if not token:
            logger.warning("微信回调Token未配置，签名验证无法通过")
        arr = [token, timestamp, nonce]
        arr.sort()
        raw = ''.join(arr)
        digest = hashlib.sha1(raw.encode('utf-8')).hexdigest()
        if digest == signature:
            return int(echostr)
        if format == "json":
            masked_token = token[:3] + "***" + token[-3:] if token else ""
            return {"code": 400, "msg": "签名验证失败", "data": {"signature": signature, "computed": digest, "timestamp": timestamp, "nonce": nonce, "token": masked_token}}
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="签名验证失败")
    # POST：事件推送或简化回调（可能无XML，仅携带openid等参数）
    params = request.query_params
    xml_content = await request.body()
    try:
        xml_tree = etree.fromstring(xml_content)
    except Exception as e:
        logger.error(f"解析微信事件XML失败: {str(e)}")
        return "success"  # 微信要求即使失败也返回success，否则会重试
    
    openid = params.get("openid")
    event_type = xml_tree.findtext("Event")  # 事件类型：SCAN（已关注扫码）、subscribe（未关注扫码后关注）
    session_id = params.get("session_id")
    event_key = xml_tree.findtext("EventKey")  # 包含scene_id的字段
    ticket = xml_tree.findtext("Ticket")  # 二维码ticket（可选，用于验证二维码有效性）

    # print(event_type,event_key,ticket, session_id,3333333333333333333333333333)
    if openid:
        # 根据 openid 进行登录/注册
        user = db.query(User).filter(User.openid == openid, User.is_delete == False).first()
        if not user:
            info = await wechat_service.get_public_user_info(openid)
            wx_sex = (info or {}).get('sex')
            gender_val = 1 if wx_sex == 1 else (2 if wx_sex == 2 else 3)
            user = User(
                openid=openid,
                wx_nickname=(info or {}).get('nickname'),
                wx_avatar=(info or {}).get('headimgurl'),
                username=((info or {}).get('nickname') or f"wx_{openid[:8]}"),
                nickname=((info or {}).get('nickname') or f"wx_{openid[:8]}"),
                avatar="/uploads/avatars/avatar.png",
                gender=gender_val,
                points=0,
                province=(info or {}).get('province'),
                city=(info or {}).get('city'),
                country=(info or {}).get('country'),
                create_time=datetime.now(),
                update_time=datetime.now()
            )
            db.add(user)
            db.commit()
            db.refresh(user)
        user.login_time = datetime.now()
        db.commit()
        # 若携带 event_key 或 session_id，则标记会话为已授权
        event_key = params.get("event_key")
        if event_key:
            ek = event_key
            if isinstance(ek, str) and ek.startswith("qrscene_"):
                ek = ek.replace("qrscene_", "", 1)
            try:
                scene_id_val = int(ek)
            except Exception:
                scene_id_val = None
            if scene_id_val:
                sess = db.query(LoginSession).filter(LoginSession.scene_id == scene_id_val).first()
                if sess and sess.expire_time >= datetime.now():
                    sess.status = "authed"
                    sess.openid = openid
                    sess.user_id = user.id
                    db.commit()
        elif session_id:
            sess = db.query(LoginSession).filter(LoginSession.session_id == session_id).first()
            if sess and sess.expire_time >= datetime.now():
                sess.status = "authed"
                sess.openid = openid
                sess.user_id = user.id
                db.commit()
    
    # 无 openid 参数则按XML事件处理
    return await wechat_event(request, format, db)


@router.post("/login", response_model=LoginResponseWrapper, summary="用户登录")
async def login(login_data: UserLogin, db: Session = Depends(get_db)):
    """
    用户登录接口
    支持用户名+密码、手机号+验证码、微信登录
    """
    user = None
    
    if login_data.login_type == LoginType.PASSWORD:
        # 用户名密码登录
        if not login_data.username or not login_data.password:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="请输入用户名和密码"
            )
        
        user = db.query(User).filter(
            User.username == login_data.username,
            User.is_delete == False
        ).first()
        
        if not user or not verify_password(login_data.password, user.password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误"
            )
    
    elif login_data.login_type == LoginType.SMS:
        # 手机号验证码登录
        if not login_data.phone or not login_data.sms_code:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="请输入手机号和验证码"
            )
        
        if not validate_phone_number(login_data.phone):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号格式不正确"
            )
        
        if not await verify_sms_code(login_data.phone, login_data.sms_code):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="验证码错误或已过期"
            )
        
        user = db.query(User).filter(
            User.phone == login_data.phone,
            User.is_delete == False
        ).first()
        # 未注册则自动注册
        if not user:
            user = User(
                phone=login_data.phone,
                nickname=f"phone_{login_data.phone[-4:]}",
                username=f"phone_{login_data.phone[-4:]}",
                avatar="/uploads/avatars/avatar.png",
                points=0,
                create_time=datetime.now(),
                update_time=datetime.now()
            )
            db.add(user)
            db.commit()
            db.refresh(user)
            try:
                create_welcome_notification(db, user.id)
            except Exception:
                pass
    
    else:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="不支持的登录方式"
        )
    
    # 更新登录时间
    user.login_time = datetime.now()
    db.commit()
    
    # 根据用户积分查询对应的等级信息
    user_level = None
    if user.points is not None:
        # 查询用户达到的最高等级（积分要求小于等于用户积分的最高等级）
        user_level = db.query(UserLevel).filter(
            UserLevel.required_points <= user.points
        ).order_by(UserLevel.required_points.desc()).first()
    
    # 查询用户的作品数量
    work_count = db.query(CompetitionWork).filter(
        CompetitionWork.user_id == user.id
    ).count()
    
    # 查询用户的关注数量
    follow_count = db.query(UserFollow).filter(
        UserFollow.follower_id == user.id,
        UserFollow.is_delete == False
    ).count()
    
    # 查询用户的粉丝数量
    fans_count = db.query(UserFollow).filter(
        UserFollow.following_id == user.id,
        UserFollow.is_delete == False
    ).count()
    
    # 统一返回对齐微信扫码登录的结构
    token_data = create_user_token(user.id, user.username or user.nickname)
    return {
        "code": 200,
        "msg": "登录成功",
        "data": {
            "status": "authed",
            "access_token": token_data["access_token"],
            "token_type": token_data["token_type"],
            "user": {
                "id": user.id,
                "points": user.points,
                "username": user.username,
                "nickname": user.nickname,
                "phone": user.phone,
                "email": user.email,
                "avatar": user.avatar or user.wx_avatar,
                "background_cover": user.background_cover,
                "create_time": user.create_time,
                "level_id": (user_level.level_id if user_level else None),
                "level_name": (user_level.level_name if user_level else None),
                "level_icon": (user_level.url if user_level else None),
                "level_icon2": (user_level.url2 if user_level else None),
                "work_count": work_count,
                "follow_count": follow_count,
                "fans_count": fans_count
            }
        }
    }


@router.post("/send-sms", summary="发送短信验证码")
async def send_sms(sms_request: SMSCodeRequest):
    """
    发送短信验证码
    """
    if not validate_phone_number(sms_request.phone):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="手机号格式不正确"
        )
    result = await send_sms_code(sms_request.phone, "login")
    if not (isinstance(result, dict) and result.get("success")):
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="短信发送失败，请稍后重试"
        )
    return {"code": 200, "msg": "验证码发送成功"}


@router.post("/send-email", summary="发送邮箱验证码")
async def send_email(email_request: EmailCodeRequest):
    """
    发送邮箱验证码
    """
    if not validate_email(email_request.email):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="邮箱格式不正确"
        )
    
    # 生成验证码
    code = generate_verification_code()
    
    # 发送邮件
    success = send_email_verification_code(email_request.email, code)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="邮件发送失败，请稍后重试"
        )
    
    return {
        "code": 200,
        "msg": "邮箱验证码发送成功"
    }


@router.get("/me", response_model=UserMeResponse, summary="获取当前用户信息")
async def get_me(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    """
    获取当前登录用户信息
    根据用户积分查询对应的等级信息
    """
    # 根据用户积分查询对应的等级信息
    user_level = None
    if current_user.points is not None:
        # 查询用户达到的最高等级（积分要求小于等于用户积分的最高等级）
        user_level = db.query(UserLevel).filter(
            UserLevel.required_points <= current_user.points
        ).order_by(UserLevel.required_points.desc()).first()
    
    # 查询用户的作品数量
    work_count = db.query(CompetitionWork).filter(
        CompetitionWork.user_id == current_user.id
    ).count()
    
    # 查询用户的关注数量
    follow_count = db.query(UserFollow).filter(
        UserFollow.follower_id == current_user.id,
        UserFollow.is_delete == False
    ).count()
    
    # 查询用户的粉丝数量
    fans_count = db.query(UserFollow).filter(
        UserFollow.following_id == current_user.id,
        UserFollow.is_delete == False
    ).count()
    
    
    
    return {
        "code": 200,
        "msg": "获取用户信息成功",
        "data": UserResponse(
            id=current_user.id,
            username=current_user.username,
            nickname=current_user.nickname,
            phone=current_user.phone,
            email=current_user.email,
            wx_id=current_user.wx_id,
            qq_id=current_user.qq_id,
            avatar=current_user.avatar or current_user.wx_avatar,
            background_cover=current_user.background_cover,
            gender=current_user.gender,
            age=current_user.age,
            points=current_user.points,
            country=current_user.country,
            province=current_user.province,
            city=current_user.city,
            district=current_user.district,
            address=current_user.address,
            birthday=current_user.birthday,
            signature=current_user.signature,
            description=current_user.description,
            xhs_id=current_user.xhs_id,
            dy_id=current_user.dy_id,
            wb_id=current_user.wb_id,
            create_time=current_user.create_time,
            level_id=user_level.level_id if user_level else None,
            level_name=user_level.level_name if user_level else None,
            level_icon=user_level.url if user_level else None,
            level_icon2=user_level.url2 if user_level else None,
            work_count=work_count,
            follow_count=follow_count,
            fans_count=fans_count
        )
    }


@router.put("/me", response_model=UserMeResponse, summary="更新用户信息")
async def update_me(
    user_update: UserUpdate,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    更新当前用户信息
    根据用户积分查询对应的等级信息
    """
    # 检查手机号是否被其他用户使用
    if user_update.phone and user_update.phone != current_user.phone:
        if not validate_phone_number(user_update.phone):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号格式不正确"
            )
        
        existing_phone = db.query(User).filter(
            User.phone == user_update.phone,
            User.id != current_user.id,
            User.is_delete == False
        ).first()
        if existing_phone:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号已被其他用户使用"
            )
    
    # 检查邮箱是否被其他用户使用
    if user_update.email and user_update.email != current_user.email:
        if not validate_email(user_update.email):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱格式不正确"
            )
        
        existing_email = db.query(User).filter(
            User.email == user_update.email,
            User.id != current_user.id,
            User.is_delete == False
        ).first()
        if existing_email:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱已被其他用户使用"
            )
    
    # 更新用户信息
    update_data = user_update.dict(exclude_unset=True)
    for field, value in update_data.items():
        if hasattr(current_user, field):
            if field in ("avatar", "background_cover"):
                safe_replace_file(current_user, field, value)
            else:
                setattr(current_user, field, value)
    
    current_user.update_time = datetime.now()
    db.commit()
    db.refresh(current_user)
    
    # 根据用户积分查询对应的等级信息
    user_level = None
    if current_user.points is not None:
        # 查询用户达到的最高等级（积分要求小于等于用户积分的最高等级）
        user_level = db.query(UserLevel).filter(
            UserLevel.required_points <= current_user.points
        ).order_by(UserLevel.required_points.desc()).first()
    
    # 查询用户的作品数量
    work_count = db.query(CompetitionWork).filter(
        CompetitionWork.user_id == current_user.id
    ).count()
    
    # 查询用户的关注数量
    follow_count = db.query(UserFollow).filter(
        UserFollow.follower_id == current_user.id,
        UserFollow.is_delete == False
    ).count()
    
    # 查询用户的粉丝数量
    fans_count = db.query(UserFollow).filter(
        UserFollow.following_id == current_user.id,
        UserFollow.is_delete == False
    ).count()
    
    user_data = UserResponse(
        id=current_user.id,
        username=current_user.username,
        nickname=current_user.nickname,
        phone=current_user.phone,
        email=current_user.email,
        wx_id=current_user.wx_id,
        qq_id=current_user.qq_id,
        avatar=current_user.avatar or current_user.wx_avatar,
        background_cover=current_user.background_cover,
        gender=current_user.gender,
        age=current_user.age,
        points=current_user.points,
        country=current_user.country,
        province=current_user.province,
        city=current_user.city,
        district=current_user.district,
        address=current_user.address,
        birthday=current_user.birthday,
        signature=current_user.signature,
        description=current_user.description,
        xhs_id=current_user.xhs_id,
        dy_id=current_user.dy_id,
        wb_id=current_user.wb_id,
        create_time=current_user.create_time,
        level_id=user_level.level_id if user_level else None,
        level_name=user_level.level_name if user_level else None,
        level_icon=user_level.url if user_level else None,
        level_icon2=user_level.url2 if user_level else None,
        work_count=work_count,
        follow_count=follow_count,
        fans_count=fans_count
    )
    
    return {
        "code": 200,
        "msg": "用户信息更新成功",
        "data": user_data
    }

# 绑定微信，qq，邮箱 的账号信息

@router.post("/bind", summary="绑定账号信息")
async def bind_account(
    bind_data: BindAccountRequest,
    current_user: dict = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    根据access_token绑定用户的微信号、QQ号或邮箱
    有传入的字段则进行绑定操作
    """
    try:
        user = db.query(User).filter(User.id == current_user.id).first()
        if not user:
            return {
                "code": 404,
                "msg": "用户不存在"
            }
        
        # 绑定微信号
        if bind_data.wx_id:
            # 检查微信号是否已被其他用户绑定
            existing_user = db.query(User).filter(
                User.wx_id == bind_data.wx_id,
                User.id != current_user.id
            ).first()
            if existing_user:
                return {
                    "code": 400,
                    "msg": "该微信号已被其他用户绑定"
                }
            user.wx_id = bind_data.wx_id
        
        # 绑定QQ号
        if bind_data.qq_id:
            # 检查QQ号是否已被其他用户绑定
            existing_user = db.query(User).filter(
                User.qq_id == bind_data.qq_id,
                User.id != current_user.id
            ).first()
            if existing_user:
                return {
                    "code": 400,
                    "msg": "该QQ号已被其他用户绑定"
                }
            user.qq_id = bind_data.qq_id

        # 绑定微博号
        if bind_data.wb_id:
            existing_user = db.query(User).filter(
                User.wb_id == bind_data.wb_id,
                User.id != current_user.id
            ).first()
            if existing_user:
                return {
                    "code": 400,
                    "msg": "该微博号已被其他用户绑定"
                }
            user.wb_id = bind_data.wb_id
        
        # 绑定邮箱
        if bind_data.email:
            # 验证邮箱格式
            if not validate_email(bind_data.email):
                return {
                    "code": 400,
                    "msg": "邮箱格式不正确"
                }
            
            # 验证邮箱验证码
            if not bind_data.email_code:
                return {
                    "code": 400,
                    "msg": "请提供邮箱验证码"
                }
            
            if not verify_email_code(bind_data.email, bind_data.email_code):
                return {
                    "code": 400,
                    "msg": "邮箱验证码错误或已过期"
                }
            
            # 检查邮箱是否已被其他用户绑定
            existing_user = db.query(User).filter(
                User.email == bind_data.email,
                User.id != current_user.id
            ).first()
            if existing_user:
                return {
                    "code": 400,
                    "msg": "该邮箱已被其他用户绑定"
                }
            user.email = bind_data.email
        
        db.commit()
        db.refresh(user)
        
        return {
            "code": 200,
            "msg": "绑定成功",
            "data": {
                "wx_id": user.wx_id,
                "qq_id": user.qq_id,
                "email": user.email,
                "wb_id": user.wb_id
            }
        }
    
    except Exception as e:
        db.rollback()
        return {
            "code": 500,
            "msg": f"绑定失败: {str(e)}"
        }


@router.post("/unbind", summary="解绑账号信息")
async def unbind_account(
    unbind_data: UnbindAccountRequest,
    current_user: dict = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    根据access_token解绑用户的微信号、QQ号或邮箱
    传入True的字段则进行解绑操作
    """
    try:
        user = db.query(User).filter(User.id == current_user.id).first()
        if not user:
            return {
                "code": 404,
                "msg": "用户不存在"
            }
        
        # 解绑微信号
        if unbind_data.wx_id:
            user.wx_id = None
        
        # 解绑QQ号
        if unbind_data.qq_id:
            user.qq_id = None
        
        # 解绑邮箱
        if unbind_data.email:
            user.email = None
        
        db.commit()
        db.refresh(user)
        
        return {
            "code": 200,
            "msg": "解绑成功",
            "data": {
                "wx_id": user.wx_id,
                "qq_id": user.qq_id,
                "email": user.email
            }
        }
    
    except Exception as e:
        db.rollback()
        return {
            "code": 500,
            "msg": f"解绑失败: {str(e)}"
        }



@router.get("/my-works", summary="获取我的作品列表")
async def get_my_works(
    page: int = Query(1, ge=1, description="页码"),
    size: int = Query(10, ge=1, le=100, description="每页数量"),
    work_type: Optional[str] = Query(None, description="作品类型筛选"),
    work_status: Optional[int] = Query(None, description="作品状态筛选"),
    work_audit_status: Optional[int] = Query(None, description="审核状态筛选"),
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    根据token查询当前用户的所有作品
    支持分页、类型筛选、状态筛选等功能
    
    Args:
        page: 页码
        size: 每页数量
        work_type: 作品类型筛选
        work_status: 作品状态筛选
        work_audit_status: 审核状态筛选
        current_user: 当前用户信息
        db: 数据库会话
    
    Returns:
        dict: 包含作品列表和分页信息的响应
    """
    try:
        # 构建查询条件
        query = db.query(CompetitionWork).filter(
            CompetitionWork.user_id == current_user.id,
            CompetitionWork.delete_time.is_(None)
        )
        
        # 作品类型筛选
        if work_type:
            query = query.filter(CompetitionWork.work_type == work_type)
        
        # 作品状态筛选
        if work_status is not None:
            query = query.filter(CompetitionWork.work_status == work_status)
        
        # 审核状态筛选
        if work_audit_status is not None:
            query = query.filter(CompetitionWork.work_audit_status == work_audit_status)
        
        # 按创建时间倒序排列
        query = query.order_by(CompetitionWork.create_time.desc())
        
        # 获取总数
        total = query.count()
        
        # 分页
        offset = (page - 1) * size
        works = query.offset(offset).limit(size).all()
        
        # 计算总页数
        pages = (total + size - 1) // size if total > 0 else 1
        
        # 格式化作品数据
        formatted_works = []
        for work in works:
            work_dict = {
                "id": work.id,
                "competition_id": work.competition_id,
                "user_id": work.user_id,
                "work_name": work.work_name,
                "isPinned": work.isPinned,  # 作品是否置顶
                "work_description": work.work_description,
                "work_file": work.work_file,
                "work_type": work.work_type,
                "work_cover": work.work_cover,
                "work_status": work.work_status,
                "work_audit_status": work.work_audit_status,
                "work_sort": work.work_sort,
                "xhs_url": work.xhs_url,
                "dy_url": work.dy_url,
                "wb_url": work.wb_url,
                "sph_url": work.sph_url,
                "xhs_like": work.xhs_like,
                "dy_like": work.dy_like,
                "wb_like": work.wb_like,
                "sph_like": work.sph_like,
                "xhs_collect": work.xhs_collect,
                "dy_collect": work.dy_collect,
                "sph_collect": work.sph_collect,
                "xhs_comment": work.xhs_comment,
                "dy_comment": work.dy_comment,
                "wb_comment": work.wb_comment,
                "sph_comment": work.sph_comment,
                "wb_zf": work.wb_zf,
                "work_like": work.work_like,
                "work_comment": work.work_comment,
                "work_score": work.work_score,
                "work_rank": work.work_rank,
                "work_award": work.work_award,
                "work_views": work.work_views,  # 作品浏览量
                "work_label": work.work_label,  # 作品标签
                "release_status": work.release_status,
                "create_time": work.create_time.strftime("%Y-%m-%d %H:%M:%S") if work.create_time else None,
                "update_time": work.update_time.strftime("%Y-%m-%d %H:%M:%S") if work.update_time else None
            }
            formatted_works.append(work_dict)
        
        return {
            "code": 200,
            "msg": "获取作品列表成功",
            "data": {
                "items": formatted_works,
                "total": total,
                "page": page,
                "size": size,
                "pages": pages
            }
        }
    
    except Exception as e:
        return {
            "code": 500,
            "msg": f"获取作品列表失败: {str(e)}",
            "data": None
        }


@router.get("/my-works/next-sort", summary="获取我的作品下一个排序值")
async def get_my_works_next_sort(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    查询当前用户作品中work_sort字段的最大值并加1
    用于新建作品时设置排序值
    
    Args:
        current_user: 当前登录用户
        db: 数据库会话
    
    Returns:
        dict: 包含下一个排序值的响应
    """
    try:
        # 查询当前用户作品中work_sort字段的最大值
        max_sort_result = db.query(func.max(CompetitionWork.work_sort)).filter(
            CompetitionWork.user_id == current_user.id,
            CompetitionWork.delete_time.is_(None)
        ).scalar()
        
        # 如果没有作品或最大值为None，则从1开始
        next_sort = (max_sort_result or 0) + 1
        
        return {
            "code": 200,
            "msg": "获取下一个排序值成功",
            "data": {
                "next_sort": next_sort,
                "max_sort": max_sort_result or 0
            }
        }
        
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取下一个排序值失败: {str(e)}"
        )

from models.auth import FollowListResponse

# 我的关注
@router.get("/my-follows", response_model=FollowListResponse, summary="获取我的关注列表")
async def get_my_follows(
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(10, ge=1, le=100, description="每页数量"),
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    获取当前用户的关注列表
    
    Args:
        page: 页码，从1开始
        page_size: 每页数量，最大100
        current_user: 当前登录用户
        db: 数据库会话
    
    Returns:
        FollowListResponse: 关注用户列表响应
    """
    try:
        # 计算偏移量
        offset = (page - 1) * page_size
        
        # 查询关注的用户列表
        follows_query = db.query(UserFollow).filter(
            UserFollow.follower_id == current_user.id,
            UserFollow.is_delete == False
        )
        
        # 获取总数
        total = follows_query.count()
        
        # 获取分页数据，关联用户信息
        follows = follows_query.offset(offset).limit(page_size).all()
        
        # 获取被关注用户的详细信息
        following_ids = [follow.following_id for follow in follows]
        users = db.query(User).filter(
            User.id.in_(following_ids),
            User.is_delete == False
        ).all()
        
        # 构建用户信息字典
        user_dict = {user.id: user for user in users}
        
        # 构建响应数据
        user_responses = []
        for follow in follows:
            user = user_dict.get(follow.following_id)
            if user:
                # 查询用户等级信息
                user_level = db.query(UserLevel).filter(
                    UserLevel.required_points <= user.points
                ).order_by(UserLevel.required_points.desc()).first()
                
                # 查询用户的作品数量
                work_count = db.query(CompetitionWork).filter(
                    CompetitionWork.user_id == user.id
                ).count()
                
                # 查询用户的关注数量
                follow_count = db.query(UserFollow).filter(
                    UserFollow.follower_id == user.id,
                    UserFollow.is_delete == False
                ).count()
                
                # 查询用户的粉丝数量
                fans_count = db.query(UserFollow).filter(
                    UserFollow.following_id == user.id,
                    UserFollow.is_delete == False
                ).count()
                
                user_responses.append(UserResponse(
                    id=user.id,
                    username=user.username,
                    nickname=user.nickname,
                    avatar=user.avatar,
                    background_cover=user.background_cover,
                    phone=user.phone,
                    email=user.email,
                    gender=user.gender,
                    age=user.age,
                    country=user.country,
                    province=user.province,
                    city=user.city,
                    district=user.district,
                    address=user.address,
                    birthday=user.birthday,
                    signature=user.signature,
                    description=user.description,
                    xhs_id=user.xhs_id,
                    dy_id=user.dy_id,
                    wb_id=user.wb_id,
                    wx_id=user.wx_id,
                    qq_id=user.qq_id,
                    points=user.points,
                    level_id=user_level.level_id if user_level else None,
                    level_name=user_level.level_name if user_level else None,
                    level_icon=user_level.url if user_level else None,
                    level_icon2=user_level.url2 if user_level else None,
                    work_count=work_count,
                    follow_count=follow_count,
                    fans_count=fans_count,
                    create_time=user.create_time
                ))
        
        return FollowListResponse(
            code=200,
            msg="获取关注列表成功",
            data=user_responses,
            total=total,
            page=page,
            page_size=page_size
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取关注列表失败: {str(e)}"
        )


# 我的粉丝
@router.get("/my-fans", response_model=FollowListResponse, summary="获取我的粉丝列表")
async def get_my_fans(
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(10, ge=1, le=100, description="每页数量"),
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    获取当前用户的粉丝列表
    
    Args:
        page: 页码，从1开始
        page_size: 每页数量，最大100
        current_user: 当前登录用户
        db: 数据库会话
    
    Returns:
        FollowListResponse: 粉丝用户列表响应
    """
    try:
        # 计算偏移量
        offset = (page - 1) * page_size
        
        # 查询粉丝用户列表
        fans_query = db.query(UserFollow).filter(
            UserFollow.following_id == current_user.id,
            UserFollow.is_delete == False
        )
        
        # 获取总数
        total = fans_query.count()
        
        # 获取分页数据，关联用户信息
        fans = fans_query.offset(offset).limit(page_size).all()
        
        # 获取粉丝用户的详细信息
        follower_ids = [fan.follower_id for fan in fans]
        users = db.query(User).filter(
            User.id.in_(follower_ids),
            User.is_delete == False
        ).all()
        
        # 构建用户信息字典
        user_dict = {user.id: user for user in users}
        
        # 构建响应数据
        user_responses = []
        for fan in fans:
            user = user_dict.get(fan.follower_id)
            if user:
                # 查询用户等级信息
                user_level = db.query(UserLevel).filter(
                    UserLevel.required_points <= user.points
                ).order_by(UserLevel.required_points.desc()).first()
                
                # 查询用户的作品数量
                work_count = db.query(CompetitionWork).filter(
                    CompetitionWork.user_id == user.id
                ).count()
                
                # 查询用户的关注数量
                follow_count = db.query(UserFollow).filter(
                    UserFollow.follower_id == user.id,
                    UserFollow.is_delete == False
                ).count()
                
                # 查询用户的粉丝数量
                fans_count = db.query(UserFollow).filter(
                    UserFollow.following_id == user.id,
                    UserFollow.is_delete == False
                ).count()
                
                user_responses.append(UserResponse(
                    id=user.id,
                    username=user.username,
                    nickname=user.nickname,
                    avatar=user.avatar,
                    background_cover=user.background_cover,
                    phone=user.phone,
                    email=user.email,
                    gender=user.gender,
                    age=user.age,
                    country=user.country,
                    province=user.province,
                    city=user.city,
                    district=user.district,
                    address=user.address,
                    birthday=user.birthday,
                    signature=user.signature,
                    description=user.description,
                    xhs_id=user.xhs_id,
                    dy_id=user.dy_id,
                    wb_id=user.wb_id,
                    wx_id=user.wx_id,
                    qq_id=user.qq_id,
                    points=user.points,
                    level_id=user_level.level_id if user_level else None,
                    level_name=user_level.level_name if user_level else None,
                    level_icon=user_level.url if user_level else None,
                    level_icon2=user_level.url2 if user_level else None,
                    work_count=work_count,
                    follow_count=follow_count,
                    fans_count=fans_count,
                    create_time=user.create_time
                ))
        
        return FollowListResponse(
            code=200,
            msg="获取粉丝列表成功",
            data=user_responses,
            total=total,
            page=page,
            page_size=page_size
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"获取粉丝列表失败: {str(e)}"
        )


from models.auth import FollowRequest, FollowResponse




@router.post("/follow", response_model=FollowResponse, summary="关注/取消关注用户")
async def toggle_follow(
    follow_request: FollowRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    关注/取消关注用户接口，同时支持点赞/取消点赞功能
    根据当前关注状态自动执行关注或取消关注操作
    根据like字段执行点赞或取消点赞操作
    
    Args:
        follow_request: 关注请求数据，包含要关注的用户ID和点赞状态
        current_user: 当前登录用户
        db: 数据库会话
    
    Returns:
        FollowResponse: 关注操作响应，包含操作结果和最新的关注状态及点赞状态
    
    Raises:
        HTTPException: 参数校验失败或操作失败时抛出异常
    """
    try:
        target_user_id = follow_request.user_id
        like_action = follow_request.like
        
        # 检查目标用户是否存在
        target_user = db.query(User).filter(
            User.id == target_user_id,
            User.is_delete == False
        ).first()
        
        if not target_user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="目标用户不存在"
            )
        
        # 不能关注或点赞自己
        if target_user_id == current_user.id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="不能关注或点赞自己"
            )
        
        # 处理关注逻辑
        # 查询当前关注关系
        existing_follow = db.query(UserFollow).filter(
            UserFollow.follower_id == current_user.id,
            UserFollow.following_id == target_user_id,
            UserFollow.is_delete == False
        ).first()
        
        is_following = False
        follow_msg = ""
        
        if existing_follow:
            # 已关注，执行取消关注操作
            existing_follow.is_delete = True
            follow_msg = "取消关注成功"
            is_following = False
            
            # 更新用户关注数和粉丝数
            if current_user.follow_num > 0:
                current_user.follow_num -= 1
            if target_user.fans_num > 0:
                target_user.fans_num -= 1
        else:
            # 未关注，执行关注操作
            # 检查是否存在已删除的关注记录
            deleted_follow = db.query(UserFollow).filter(
                UserFollow.follower_id == current_user.id,
                UserFollow.following_id == target_user_id,
                UserFollow.is_delete == True
            ).first()
            
            if deleted_follow:
                # 恢复已删除的关注记录
                deleted_follow.is_delete = False
                deleted_follow.follow_time = datetime.now()
            else:
                # 创建新的关注记录
                new_follow = UserFollow(
                    follower_id=current_user.id,
                    following_id=target_user_id,
                    follow_time=datetime.now(),
                    is_delete=False
                )
                db.add(new_follow)
            
            follow_msg = "关注成功"
            is_following = True
            
            # 更新用户关注数和粉丝数
            current_user.follow_num = (current_user.follow_num or 0) + 1
            target_user.fans_num = (target_user.fans_num or 0) + 1
            
            # 创建关注通知
            try:
                notification = UserNotification(
                    user_id=str(target_user_id),  # 被关注用户ID，存储为字符串格式
                    sender_id=current_user.id,  # 关注用户
                    notification_type=3,  # 3-关注消息（根据模型注释）
                    title='关注通知',
                    content=f'{current_user.nickname or current_user.username or "用户"} 关注了你',
                    related_id=current_user.id,
                    related_type='user_follow',
                    is_read_id=None,  # 初始时没有用户已读
                    is_delete_id=None  # 初始时没有用户删除该通知
                )
                db.add(notification)
            except Exception as notification_error:
                # 通知创建失败不影响关注功能
                print(f"创建关注通知失败: {str(notification_error)}")
        
        # 处理点赞逻辑
        # 查询当前点赞关系
        existing_like = db.query(UserLike).filter(
            UserLike.liker_user_id == current_user.id,
            UserLike.target_user_id == target_user_id
        ).first()
        
        is_liked = False
        like_msg = ""
        
        if like_action:
            # 执行点赞操作
            if existing_like:
                # 已经有记录，只有从未点赞 -> 点赞时才增加计数
                if not existing_like.is_like:
                    existing_like.is_like = True
                    existing_like.update_time = datetime.now()
                    like_msg = "点赞成功"
                    target_user.like_num = (target_user.like_num or 0) + 1
                else:
                    existing_like.update_time = datetime.now()
                    like_msg = "已点赞"
            else:
                # 创建新的点赞记录
                new_like = UserLike(
                    target_user_id=target_user_id,
                    liker_user_id=current_user.id,
                    is_like=True
                )
                db.add(new_like)
                like_msg = "点赞成功"
                target_user.like_num = (target_user.like_num or 0) + 1
            
            is_liked = True
            
            # 创建点赞通知
            try:
                notification = UserNotification(
                    user_id=str(target_user_id),  # 被点赞用户ID
                    sender_id=current_user.id,  # 点赞用户
                    notification_type=4,  # 4-点赞消息（假设类型）
                    title='点赞通知',
                    content=f'{current_user.nickname or current_user.username or "用户"} 点赞了你',
                    related_id=current_user.id,
                    related_type='user_like',
                    is_read_id=None,
                    is_delete_id=None
                )
                db.add(notification)
            except Exception as notification_error:
                # 通知创建失败不影响点赞功能
                print(f"创建点赞通知失败: {str(notification_error)}")
        else:
            # 执行取消点赞操作
            if existing_like and existing_like.is_like:
                existing_like.is_like = False
                existing_like.update_time = datetime.now()
                like_msg = "取消点赞成功"
                is_liked = False
                if (target_user.like_num or 0) > 0:
                    target_user.like_num -= 1
            else:
                like_msg = "未点赞，无需取消"
                is_liked = False
        
        # 提交数据库更改
        db.commit()
        
        # 重新查询最新的关注数和粉丝数
        follow_count = db.query(UserFollow).filter(
            UserFollow.follower_id == current_user.id,
            UserFollow.is_delete == False
        ).count()
        
        fans_count = db.query(UserFollow).filter(
            UserFollow.following_id == target_user_id,
            UserFollow.is_delete == False
        ).count()
        
        # 重新查询最新的点赞数
        like_count = db.query(UserLike).filter(
            UserLike.target_user_id == target_user_id,
            UserLike.is_like == True
        ).count()
        
        # 组合消息
        combined_msg = f"{follow_msg}，{like_msg}" if follow_msg and like_msg else (follow_msg or like_msg)
        
        return FollowResponse(
            code=200,
            msg=combined_msg,
            is_following=is_following,
            follow_count=follow_count,
            fans_count=fans_count,
            is_liked=is_liked,
            like_count=like_count
        )
        
    except HTTPException:
        # 重新抛出HTTP异常
        raise
    except Exception as e:
        # 回滚数据库事务
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"操作失败: {str(e)}"
        )


from models.auth import BackgroundCoverUpdate, BackgroundCoverResponse

# 修改background_cover

@router.put("/me/background-cover", response_model=BackgroundCoverResponse, summary="更新用户背景封面")
async def update_background_cover(
    background_data: BackgroundCoverUpdate,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    更新用户背景封面
    
    Args:
        background_data: 背景封面更新数据
        current_user: 当前登录用户
        db: 数据库会话
        
    Returns:
        BackgroundCoverResponse: 更新结果
    """
    try:
        # 更新用户背景封面（自动删除旧文件）
        safe_replace_file(current_user, 'background_cover', background_data.background_cover)
        current_user.update_time = datetime.now()
        
        # 提交数据库更改
        db.commit()
        db.refresh(current_user)
        
        return BackgroundCoverResponse(
            code=200,
            msg="背景封面更新成功",
            data={
                "background_cover": current_user.background_cover,
                "update_time": current_user.update_time.isoformat() if current_user.update_time else None
            }
        )
        
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"背景封面更新失败: {str(e)}"
        )


# ============ 首页轮播图公开接口 =============

@router.get("/banners", summary="获取首页轮播图列表")
async def get_public_banners(
    db: Session = Depends(get_db)
):
    """
    获取首页轮播图列表（公开接口，无需认证）
    只返回启用状态且启用时间小于等于当前时间的轮播图
    
    Args:
        db: 数据库会话
        
    Returns:
        dict: 轮播图列表响应
    """
    try:
        # 获取当前时间
        current_time = datetime.now()
        
        # 查询条件：未删除、已启用、启用时间小于等于当前时间
        banners = db.query(Banner).filter(
            Banner.is_delete == False,
            Banner.is_active == True,
            Banner.enable_time <= current_time
        ).order_by(Banner.sort_order.asc(), Banner.create_time.desc()).all()
        
        # 构建响应数据
        banner_list = []
        for banner in banners:
            banner_list.append({
                "id": banner.id,
                "title": banner.title,
                "image_url": banner.image_url,
                "link_url": banner.link_url,
                "description": banner.description,
                "sort_order": banner.sort_order
            })
        
        return {
            "code": 200,
            "msg": "获取轮播图列表成功",
            "data": banner_list
        }
        
    except Exception as e:
        return {
            "code": 500,
            "msg": f"获取轮播图列表失败: {str(e)}"
        }
@router.get("/wechat/authorize", summary="生成微信公众号网页授权地址")
async def wechat_authorize(
    redirect_uri: str = Query(..., description="授权回调地址"),
    scope: str = Query("snsapi_userinfo", description="授权范围"),
    client_redirect: Optional[str] = Query(None, description="前端接收结果的重定向地址"),
    db: Session = Depends(get_db)
):
    """
    生成微信公众号网页授权地址（用于扫码登录）

    Args:
        redirect_uri: 授权回调地址（需在微信平台配置白名单）
        scope: 授权范围（默认 snsapi_userinfo）

    Returns:
        dict: 包含 authorize_url 与 state 的数据
    """
    settings = get_settings()
    if not settings.wechat_public_app_id:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="公众号配置缺失")

    # 生成后端会话ID并预建登录会话
    session_id = uuid.uuid4().hex
    expire_seconds = 600
    expire_time = datetime.now() + timedelta(seconds=expire_seconds)
    db.add(LoginSession(session_id=session_id, scene_id=0, status="pending", expire_time=expire_time))
    db.commit()

    # 生成并保存 state（包含session_id，防止 CSRF）
    encoded_client_redirect = quote_plus(client_redirect) if client_redirect else ""
    state = f"{uuid.uuid4().hex}|{session_id}|{encoded_client_redirect}"
    db.add(OauthState(state=state, expire_time=expire_time))
    db.commit()

    encoded_redirect = quote_plus(redirect_uri)
    authorize_url = (
        f"https://open.weixin.qq.com/connect/oauth2/authorize?appid={settings.wechat_public_app_id}"
        f"&redirect_uri={encoded_redirect}&response_type=code&scope={scope}&state={state}#wechat_redirect"
    )

    return {"code": 200, "msg": "授权地址生成成功", "data": {"authorize_url": authorize_url, "state": state, "session_id": session_id, "expire_seconds": expire_seconds, "client_redirect": client_redirect}}


@router.get("/wechat/callback", response_model=TokenResponse, summary="微信公众号网页授权回调")
async def wechat_callback(code: str = Query(..., description="授权码"), state: str = Query(..., description="授权状态"), db: Session = Depends(get_db)):
    """
    微信公众号网页授权回调处理

    Args:
        code: 微信回调授权码
        state: 之前生成的授权状态（用于防止 CSRF）
        db: 数据库会话

    Returns:
        TokenResponse: 登录成功后返回令牌与用户信息
    """
    # 校验 state（数据库）
    oauth_state = db.query(OauthState).filter(OauthState.state == state).first()
    if not oauth_state or oauth_state.expire_time < datetime.now():
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="授权状态无效或已过期")
    # 从state中解析session_id（格式：uuid|session_id）
    try:
        parts = state.split("|")
        session_id = parts[1] if len(parts) >= 2 else None
        client_redirect = unquote_plus(parts[2]) if len(parts) >= 3 and parts[2] else None
    except Exception:
        session_id = None
        client_redirect = None
    db.delete(oauth_state)
    db.commit()

    # 使用 OAuth2 交换用户 access_token
    token_info = await wechat_service.oauth_access_token(code)
    if not token_info or not token_info.get("access_token") or not token_info.get("openid"):
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="授权码交换失败")

    access_token = token_info["access_token"]
    openid = token_info["openid"]

    wechat_user = await wechat_service.oauth_userinfo(access_token, openid)
    sub_info = await wechat_service.get_public_user_info(openid)

    if sub_info and sub_info.get('openid') and sub_info.get('nickname') is not None:
        user = db.query(User).filter(User.openid == openid, User.is_delete == False).first()
        if not user:
            wx_sex = (wechat_user or {}).get('sex')
            gender_val = 1 if wx_sex == 1 else (2 if wx_sex == 2 else 3)
            user = User(
                openid=openid,
                wx_nickname=(wechat_user or {}).get('nickname'),
                wx_avatar=(wechat_user or {}).get('headimgurl'),
                nickname=(wechat_user or {}).get('nickname') or f"微信用户_{openid[:8]}",
                gender=gender_val,
                province=(wechat_user or {}).get('province'),
                city=(wechat_user or {}).get('city'),
                country=(wechat_user or {}).get('country'),
                create_time=datetime.now(),
                update_time=datetime.now()
            )
            db.add(user)
            db.commit()
            db.refresh(user)
            try:
                create_welcome_notification(db, user.id)
            except Exception:
                pass
        user.login_time = datetime.now()
        db.commit()
        # 若有session_id，标记会话已授权
        if session_id:
            sess = db.query(LoginSession).filter(LoginSession.session_id == session_id).first()
            if sess and sess.expire_time >= datetime.now():
                sess.status = "authed"
                sess.openid = openid
                sess.user_id = user.id
                db.commit()
        token_data = create_user_token(user.id, user.username or user.nickname)
        if client_redirect:
            redirect_url = f"{client_redirect}?code=200&token={quote_plus(token_data['access_token'])}&token_type={token_data['token_type']}&user_id={user.id}&session_id={session_id or ''}"
            return RedirectResponse(url=redirect_url, status_code=302)
        return TokenResponse(
            code=200,
            access_token=token_data["access_token"],
            token_type=token_data["token_type"],
            user=UserResponse(
                id=user.id,
                points=user.points,
                username=user.username,
                nickname=user.nickname,
                phone=user.phone,
                email=user.email,
                avatar=user.avatar or user.wx_avatar,
                background_cover=user.background_cover,
                create_time=user.create_time
            )
        )
    else:
        # 未关注时：为现有会话生成二维码（复用session_id）
        if not session_id:
            session_id = uuid.uuid4().hex
            expire_seconds = 300
            expire_time = datetime.now() + timedelta(seconds=expire_seconds)
            db.add(LoginSession(session_id=session_id, scene_id=0, status="pending", expire_time=expire_time))
            db.commit()
        scene_id = random.randint(10000000, 99999999)
        expire_seconds = 300
        expire_time = datetime.now() + timedelta(seconds=expire_seconds)
        sess = db.query(LoginSession).filter(LoginSession.session_id == session_id).first()
        if sess:
            sess.scene_id = scene_id
            sess.status = "pending"
            sess.expire_time = expire_time
            db.commit()
        else:
            db.add(LoginSession(session_id=session_id, scene_id=scene_id, status="pending", expire_time=expire_time))
            db.commit()
        qr = await wechat_service.create_qr_scene(scene_id, expire_seconds)
        if not qr:
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="生成关注二维码失败")
        if client_redirect:
            redirect_url = f"{client_redirect}?code=201&session_id={session_id}&scene_id={scene_id}&qr_url={quote_plus(qr['qr_url'])}&expire_seconds={qr['expire_seconds']}"
            return RedirectResponse(url=redirect_url, status_code=302)
        return {
            "code": 201,
            "msg": "未关注，请扫码关注后自动登录",
            "data": {
                "session_id": session_id,
                "scene_id": scene_id,
                "qr_url": qr["qr_url"],
                "expire_seconds": qr["expire_seconds"]
            }
        }


@router.get("/wechat/stable-token", summary="获取稳定版公众号access_token")
async def get_stable_token(force_refresh: bool = Query(False, description="是否强制刷新")):
    """
    获取稳定版接口调用凭据（getStableAccessToken）
    
    Args:
        force_refresh: 是否强制刷新（默认False）
    
    Returns:
        统一返回结构：{"code":200,"msg":"ok","data":{"access_token":...,"expires_in":...}}
    """
    token_info = await wechat_service.get_stable_access_token(force_refresh=force_refresh)
    if not token_info:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="获取稳定版access_token失败")
    return {"code": 200, "msg": "ok", "data": token_info}
def build_text_reply_xml(to_user: str, from_user: str, content: str) -> str:
    """
    构造被动回复文本消息的XML

    Args:
        to_user: 接收方用户的 openid（Event 中的 FromUserName）
        from_user: 开发者公众号原始ID（Event 中的 ToUserName）
        content: 文本内容

    Returns:
        XML 字符串，符合微信被动回复规范
    """
    ts = int(datetime.now().timestamp())
    return (
        f"<xml>"
        f"<ToUserName><![CDATA[{to_user}]]></ToUserName>"
        f"<FromUserName><![CDATA[{from_user}]]></FromUserName>"
        f"<CreateTime>{ts}</CreateTime>"
        f"<MsgType><![CDATA[text]]></MsgType>"
        f"<Content><![CDATA[{content}]]></Content>"
        f"</xml>"
    )
def decrypt_wechat_xml(encrypt_text: str, msg_signature: str, timestamp: str, nonce: str, token: str, aes_key: str, app_id: str) -> Optional[str]:
    """
    解密微信加密消息，返回解密后的 XML 字符串

    Args:
        encrypt_text: 加密的消息密文（XML 中的 Encrypt 字段）
        msg_signature: 微信提供的消息签名（用于校验）
        timestamp: 时间戳
        nonce: 随机数
        token: 开发者设置的 Token（与微信后台一致）
        aes_key: EncodingAESKey（43位字符串）
        app_id: 公众号原始ID（用于校验解密结果尾部的 AppID）

    Returns:
        成功返回解密后的 XML 字符串；失败返回 None
    """
    try:
        import hashlib
        arr = [token or "", timestamp or "", nonce or "", encrypt_text or ""]
        arr.sort()
        raw = ''.join(arr)
        sha = hashlib.sha1(raw.encode('utf-8')).hexdigest()
        if msg_signature and sha != msg_signature:
            return None
        try:
            from base64 import b64decode
            from Crypto.Cipher import AES
        except Exception:
            return None
        if not aes_key or not app_id:
            return None
        key = b64decode(aes_key + "=")
        iv = key[:16]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        decrypted = cipher.decrypt(b64decode(encrypt_text))
        pad = decrypted[-1]
        pad_len = pad if isinstance(pad, int) else ord(pad)
        if pad_len < 1 or pad_len > 32:
            return None
        decrypted = decrypted[:-pad_len]
        content = decrypted[16:]
        msg_len = int.from_bytes(content[:4], byteorder='big')
        xml_data = content[4:4 + msg_len].decode('utf-8', errors='ignore')
        tail_appid = content[4 + msg_len:].decode('utf-8', errors='ignore')
        if app_id and tail_appid != app_id:
            return None
        return xml_data
    except Exception:
        return None
